﻿Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.Xml
Imports System.Xml.XPath

Namespace Processors

    Public Class ColladaMesh

        Private Structure AssetData
            Public Copyright As String
            Public AuthoringTool As String
            Public SourceData As String
            Public UpAxis As String
            Public UnitName As String
            Public UnitMeter As String
            Public Author As String
            Public DateCreated As String
            Public DateModified As String
            Public Comments As String
        End Structure

        Private Structure EffectData
            Public Name As String
            Public ID As String
            Public TechniqueSID As String
            Public Emission As Color
            Public Ambient As Color
            Public Specular As Color
            Public Shininess As Single
            Public Diffuse As Color
            Public Reflective As Color
            Public Reflectivity As Single
            Public Transparent As Color
            Public Transparency As Single
        End Structure

        Private Structure MaterialData
            Public ID As String
            Public EffectURL As String
        End Structure

        Private Structure GeometryData
            Public ID As String
            Public Name As String
            Public Source() As SourceData
            Public SourceCount As Integer
            Public Vertices() As VertexData
            Public MeshParts() As MeshPartData
            Public MeshCount As Integer
        End Structure

        Private Structure MeshPartData
            Public ID As String
            Public VertexCount As Integer
            Public VertexOffset As Integer
            Public NormalOffset As Integer
            Public TextureOffset As Integer
            Public VertexSource As String
            Public NormalSource As String
            Public TextCoordSource As String
            Public Positions() As Integer
            Public Normals() As Integer
            Public TextCoords() As Integer
        End Structure

        Private Structure SourceData
            Public ID As String
            Public Data() As Single
            Public Count As Integer
            Public Stride As Integer
        End Structure

        Private Structure VertexData
            Public Vertex As Integer
            Public Normal As Integer
            Public TextureCoordinate As Integer
        End Structure

        Private ModObject() As MapObjects.Models.ModelObject
        Private G As GraphicsDeviceManager
        Private ContentDir As String

        Public Sub New(Graphics As GraphicsDeviceManager, ContentDirectory As String)
            G = Graphics
            ContentDir = ContentDirectory
        End Sub

        Public Function GetModelObjects() As MapObjects.Models.ModelObject()
            Return ModObject
        End Function

        Public Sub ImportDAEFile(FileName As String, Scale As Single)
            Dim s() As String
            Dim XDoc As XmlDocument
            Dim XNode1 As XmlNode
            Dim XNode2 As XmlNode
            Dim XNode3 As XmlNode
            Dim XNode4 As XmlNode
            Dim XNode5 As XmlNode
            Dim XNode6 As XmlNode
            Dim XNode7 As XmlNode
            Dim Root As XmlNode
            Dim Assets As AssetData
            Dim Effects() As EffectData
            Dim EffectCt As Integer = -1
            Dim Materials() As MaterialData
            Dim MaterialCt As Integer = -1
            Dim Geometries() As GeometryData
            Dim GeometryCt As Integer = -1
            Dim PositionSource As String
            Dim ItemCt As Integer = 0
            'On Error Resume Next
            XDoc = New XmlDocument
            XDoc.Load(ContentDir & FileName & ".dae")
            Root = XDoc.DocumentElement
            For Each XNode1 In Root.ChildNodes
                Select Case XNode1.Name
                    Case "asset"
                        For Each XNode2 In XNode1.ChildNodes
                            Select Case XNode2.Name
                                Case "contributor"
                                    For Each XNode3 In XNode2.ChildNodes
                                        Select Case XNode3.Name
                                            Case "author"
                                                Assets.Author = XNode3.InnerText
                                            Case "authoring_tool"
                                                Assets.AuthoringTool = XNode3.InnerText
                                            Case "comments"
                                                Assets.Comments = XNode3.InnerText
                                            Case "copyright"
                                                Assets.Copyright = XNode3.InnerText
                                            Case "source_data"
                                                Assets.SourceData = XNode3.InnerText
                                        End Select
                                    Next
                                Case "modified"
                                    Assets.DateModified = XNode2.InnerText
                                Case "created"
                                    Assets.DateCreated = XNode2.InnerText
                                Case "unit"
                                    Assets.UnitMeter = XNode2.Attributes("meter").Value
                                    'Assets.UnitName = XNode2.Attributes("name").Value
                                Case "up_axis"
                                    Assets.UpAxis = XNode2.InnerText
                            End Select
                        Next
                    Case "library_effects"
                        For Each XNode2 In XNode1.ChildNodes
                            Select Case XNode2.Name
                                Case "effect"
                                    EffectCt = EffectCt + 1
                                    ReDim Preserve Effects(EffectCt)
                                    Effects(EffectCt).ID = XNode2.Attributes("id").Value
                                    'Effects(EffectCt).Name = XNode2.Attributes("name").Value
                                    For Each XNode3 In XNode2.ChildNodes
                                        Select Case XNode3.Name
                                            Case "profile_COMMON"
                                                For Each XNode4 In XNode3.ChildNodes
                                                    Select Case XNode4.Name
                                                        Case "technique"
                                                            Effects(EffectCt).TechniqueSID = XNode4.Attributes("sid").Value
                                                            For Each XNode5 In XNode4.ChildNodes
                                                                Select Case XNode5.Name
                                                                    Case "phong"
                                                                        For Each XNode6 In XNode5.ChildNodes
                                                                            Select Case XNode6.Name
                                                                                Case "emission"
                                                                                    For Each XNode7 In XNode6.ChildNodes
                                                                                        Select Case XNode7.Name
                                                                                            Case "color"
                                                                                                s = Split(XNode7.InnerText, " ")
                                                                                                Effects(EffectCt).Emission = New Color(CalculateIntFromSingle(s(0)), CalculateIntFromSingle(s(1)), CalculateIntFromSingle(s(2)), CalculateIntFromSingle(s(3)))
                                                                                        End Select
                                                                                    Next
                                                                                Case "ambient"
                                                                                    For Each XNode7 In XNode6.ChildNodes
                                                                                        Select Case XNode7.Name
                                                                                            Case "color"
                                                                                                s = Split(XNode7.InnerText, " ")
                                                                                                Effects(EffectCt).Ambient = New Color(CalculateIntFromSingle(s(0)), CalculateIntFromSingle(s(1)), CalculateIntFromSingle(s(2)), CalculateIntFromSingle(s(3)))
                                                                                        End Select
                                                                                    Next
                                                                                Case "diffuse"
                                                                                    For Each XNode7 In XNode6.ChildNodes
                                                                                        Select Case XNode7.Name
                                                                                            Case "color"
                                                                                                s = Split(XNode7.InnerText, " ")
                                                                                                Effects(EffectCt).Diffuse = New Color(CalculateIntFromSingle(s(0)), CalculateIntFromSingle(s(1)), CalculateIntFromSingle(s(2)), CalculateIntFromSingle(s(3)))
                                                                                        End Select
                                                                                    Next
                                                                                Case "specular"
                                                                                    For Each XNode7 In XNode6.ChildNodes
                                                                                        Select Case XNode7.Name
                                                                                            Case "color"
                                                                                                s = Split(XNode7.InnerText, " ")
                                                                                                Effects(EffectCt).Specular = New Color(CalculateIntFromSingle(s(0)), CalculateIntFromSingle(s(1)), CalculateIntFromSingle(s(2)), CalculateIntFromSingle(s(3)))
                                                                                        End Select
                                                                                    Next
                                                                                Case "shininess"
                                                                                    For Each XNode7 In XNode6.ChildNodes
                                                                                        Select Case XNode7.Name
                                                                                            Case "float"
                                                                                                Effects(EffectCt).Shininess = CSng(XNode7.InnerText)
                                                                                        End Select
                                                                                    Next
                                                                                Case "reflective"
                                                                                    For Each XNode7 In XNode6.ChildNodes
                                                                                        Select Case XNode7.Name
                                                                                            Case "color"
                                                                                                s = Split(XNode7.InnerText, " ")
                                                                                                Effects(EffectCt).Reflective = New Color(CalculateIntFromSingle(s(0)), CalculateIntFromSingle(s(1)), CalculateIntFromSingle(s(2)), CalculateIntFromSingle(s(3)))
                                                                                        End Select
                                                                                    Next
                                                                                Case "reflectivity"
                                                                                    For Each XNode7 In XNode6.ChildNodes
                                                                                        Select Case XNode7.Name
                                                                                            Case "float"
                                                                                                Effects(EffectCt).Reflectivity = CSng(XNode7.InnerText)
                                                                                        End Select
                                                                                    Next
                                                                                Case "transparent"
                                                                                    For Each XNode7 In XNode6.ChildNodes
                                                                                        Select Case XNode7.Name
                                                                                            Case "color"
                                                                                                s = Split(XNode7.InnerText, " ")
                                                                                                Effects(EffectCt).Transparent = New Color(CalculateIntFromSingle(s(0)), CalculateIntFromSingle(s(1)), CalculateIntFromSingle(s(2)), CalculateIntFromSingle(s(3)))
                                                                                        End Select
                                                                                    Next
                                                                                Case "transparency"
                                                                                    For Each XNode7 In XNode6.ChildNodes
                                                                                        Select Case XNode7.Name
                                                                                            Case "float"
                                                                                                Effects(EffectCt).Transparency = CSng(XNode7.InnerText)
                                                                                        End Select
                                                                                    Next
                                                                            End Select
                                                                        Next
                                                                End Select
                                                            Next
                                                    End Select
                                                Next
                                        End Select
                                    Next
                            End Select
                        Next
                    Case "library_materials"
                        For Each XNode2 In XNode1.ChildNodes
                            Select Case XNode2.Name
                                Case "material"
                                    MaterialCt = MaterialCt + 1
                                    ReDim Preserve Materials(MaterialCt)
                                    Materials(MaterialCt).ID = XNode2.Attributes("id").Value
                                    For Each XNode3 In XNode2.ChildNodes
                                        Select Case XNode3.Name
                                            Case "instance_effect"
                                                Materials(MaterialCt).EffectURL = XNode3.Attributes("url").Value
                                        End Select
                                    Next
                            End Select
                        Next
                    Case "library_geometries"
                        For Each XNode2 In XNode1.ChildNodes
                            Select Case XNode2.Name
                                Case "geometry"
                                    GeometryCt = GeometryCt + 1
                                    ReDim Preserve Geometries(GeometryCt)
                                    Geometries(GeometryCt).ID = XNode2.Attributes("id").Value
                                    Geometries(GeometryCt).Name = XNode2.Attributes("name").Value
                                    Geometries(GeometryCt).SourceCount = -1
                                    Geometries(GeometryCt).MeshCount = -1
                                    For Each XNode3 In XNode2.ChildNodes
                                        Select Case XNode3.Name
                                            Case "mesh"
                                                For Each XNode4 In XNode3.ChildNodes
                                                    Select Case XNode4.Name
                                                        Case "source"
                                                            Geometries(GeometryCt).SourceCount = Geometries(GeometryCt).SourceCount + 1
                                                            ReDim Preserve Geometries(GeometryCt).Source(Geometries(GeometryCt).SourceCount)
                                                            With Geometries(GeometryCt).Source(Geometries(GeometryCt).SourceCount)
                                                                .ID = XNode4.Attributes("id").Value
                                                                For Each XNode5 In XNode4.ChildNodes
                                                                    Select Case XNode5.Name
                                                                        Case "float_array"
                                                                            .Count = CInt(XNode5.Attributes("count").Value)
                                                                            ReDim .Data(.Count - 1)
                                                                            s = Split(Replace(Trim(XNode5.InnerText), "  ", " "), " ")
                                                                            For i = 0 To .Data.Count - 1
                                                                                .Data(i) = CSng(s(i))
                                                                            Next i
                                                                        Case "accessor"
                                                                            .Stride = CInt(XNode5.Attributes("stride").Value)
                                                                    End Select
                                                                Next
                                                            End With
                                                        Case "vertices"
                                                            For Each XNode5 In XNode4.ChildNodes
                                                                Select Case XNode5.Name
                                                                    Case "input"
                                                                        If XNode5.Attributes("semantic").Value = "POSITION" Then
                                                                            PositionSource = XNode5.Attributes("source").Value
                                                                        End If
                                                                End Select
                                                            Next
                                                        Case "polylist"
                                                            Geometries(GeometryCt).MeshCount = Geometries(GeometryCt).MeshCount + 1
                                                            ReDim Preserve Geometries(GeometryCt).MeshParts(Geometries(GeometryCt).MeshCount)
                                                            With Geometries(GeometryCt).MeshParts(Geometries(GeometryCt).MeshCount)
                                                                .NormalOffset = -1
                                                                .VertexOffset = -1
                                                                .TextureOffset = -1
                                                                .ID = XNode4.Attributes("material").Value
                                                                .VertexCount = CInt(XNode4.Attributes("count").Value)
                                                                .VertexSource = PositionSource
                                                                For Each XNode5 In XNode4.ChildNodes
                                                                    Select Case XNode5.Name
                                                                        Case "input"
                                                                            Select Case XNode5.Attributes("semantic").Value
                                                                                Case "VERTEX"
                                                                                    .VertexOffset = CInt(XNode5.Attributes("offset").Value)
                                                                                Case "NORMAL"
                                                                                    .NormalSource = XNode5.Attributes("source").Value
                                                                                    .NormalOffset = CInt(XNode5.Attributes("offset").Value)
                                                                                Case "TEXCOORD"
                                                                                    .TextCoordSource = XNode5.Attributes("source").Value
                                                                                    .TextureOffset = CInt(XNode5.Attributes("offset").Value)
                                                                            End Select
                                                                        Case "p"
                                                                            ItemCt = 0
                                                                            s = Split(Replace(XNode5.InnerText, "  ", " "), " ")
                                                                            If .VertexOffset > -1 Then
                                                                                ItemCt = ItemCt + 1
                                                                            End If
                                                                            If .NormalOffset > -1 Then
                                                                                ItemCt = ItemCt + 1
                                                                            End If
                                                                            If .TextureOffset > -1 Then
                                                                                ItemCt = ItemCt + 1
                                                                            End If
                                                                            If .VertexOffset > -1 Then
                                                                                ReDim .Positions(CInt(s.Count / ItemCt - 1))
                                                                            End If
                                                                            If .NormalOffset > -1 Then
                                                                                ReDim .Normals(CInt(s.Count / ItemCt - 1))
                                                                            End If
                                                                            If .TextureOffset > -1 Then
                                                                                ReDim .TextCoords(CInt(s.Count / ItemCt - 1))
                                                                            End If
                                                                            For i = 0 To (s.Count - ItemCt - 1) Step ItemCt
                                                                                If .VertexOffset > -1 Then
                                                                                    .Positions(CInt(i / ItemCt)) = CInt(s(i + .VertexOffset))
                                                                                End If
                                                                                If .NormalOffset > -1 Then
                                                                                    .Normals(CInt(i / ItemCt)) = CInt(s(i + .NormalOffset))
                                                                                End If
                                                                                If .TextureOffset > -1 Then
                                                                                    .TextCoords(CInt(i / ItemCt)) = CInt(s(i + .TextureOffset))
                                                                                End If
                                                                            Next i
                                                                    End Select
                                                                Next
                                                            End With

                                                    End Select
                                                Next
                                        End Select
                                    Next
                            End Select
                        Next
                End Select
            Next
            ModObject = BuildModelObject(Effects, Materials, Geometries, Scale)
        End Sub

        Private Function BuildModelObject(Effects() As EffectData, Materials() As MaterialData, Geometries() As GeometryData, Scale As Single) As MapObjects.Models.ModelObject()
            Dim MO() As MapObjects.Models.ModelObject
            Dim i As Integer, j As Integer, k As Integer
            Dim VertSourceID As Integer = -1
            Dim NormSourceID As Integer = -1
            Dim TextSourceID As Integer = -1
            Dim IDs As MapObjects.Models.IndexData
            Dim MeshCt As Integer = -1
            Dim tmpV3 As Vector3
            Dim tmpV2 As Vector2
            For i = 0 To Geometries.Count - 1
                For j = 0 To Geometries(i).MeshCount
                    MeshCt = MeshCt + 1
                    ReDim Preserve MO(MeshCt)
                    MO(MeshCt).GroupName = Geometries(i).MeshParts(j).ID
                    MO(MeshCt).Positions = New List(Of Vector3)
                    MO(MeshCt).Normals = New List(Of Vector3)
                    MO(MeshCt).TexCoords = New List(Of Vector2)
                    MO(MeshCt).Indices = New List(Of MapObjects.Models.IndexData)
                    MO(MeshCt).ModelTexture = New Texture2D(G.GraphicsDevice, 1, 1)
                    For k = 0 To Geometries(i).Source.Count - 1
                        If Geometries(i).Source(k).ID = Replace(Geometries(i).MeshParts(j).VertexSource, "#", "") Then
                            VertSourceID = k
                        End If
                        If Geometries(i).Source(k).ID = Replace(Geometries(i).MeshParts(j).NormalSource, "#", "") Then
                            NormSourceID = k
                        End If
                        If Geometries(i).Source(k).ID = Replace(Geometries(i).MeshParts(j).TextCoordSource, "#", "") Then
                            TextSourceID = k
                        End If
                    Next k
                    If Geometries(i).MeshParts(j).VertexOffset > -1 Then
                        For k = 0 To Geometries(i).MeshParts(j).Positions.Count - 1
                            tmpV3 = Vector3.Zero
                            tmpV3.X = Geometries(i).Source(VertSourceID).Data((Geometries(i).MeshParts(j).Positions(k) * 3) + 0) * Scale
                            tmpV3.Y = Geometries(i).Source(VertSourceID).Data((Geometries(i).MeshParts(j).Positions(k) * 3) + 1) * Scale
                            tmpV3.Z = Geometries(i).Source(VertSourceID).Data((Geometries(i).MeshParts(j).Positions(k) * 3) + 2) * Scale
                            MO(MeshCt).Positions.Add(tmpV3)
                        Next k
                    End If
                    If Geometries(i).MeshParts(j).NormalOffset > -1 Then
                        For k = 0 To Geometries(i).MeshParts(j).Normals.Count - 1
                            tmpV3 = Vector3.Zero
                            tmpV3.X = Geometries(i).Source(NormSourceID).Data((Geometries(i).MeshParts(j).Normals(k) * 3) + 0)
                            tmpV3.Y = Geometries(i).Source(NormSourceID).Data((Geometries(i).MeshParts(j).Normals(k) * 3) + 1)
                            tmpV3.Z = Geometries(i).Source(NormSourceID).Data((Geometries(i).MeshParts(j).Normals(k) * 3) + 2)
                            MO(MeshCt).Normals.Add(tmpV3)
                        Next k
                    End If
                    If Geometries(i).MeshParts(j).TextureOffset > -1 Then
                        For k = 0 To Geometries(i).MeshParts(j).TextCoords.Count - 1
                            tmpV2 = Vector2.Zero
                            tmpV2.X = Geometries(i).Source(TextSourceID).Data((Geometries(i).MeshParts(j).TextCoords(k) * 2) + 0)
                            tmpV2.Y = Geometries(i).Source(TextSourceID).Data((Geometries(i).MeshParts(j).TextCoords(k) * 2) + 1)
                            MO(MeshCt).TexCoords.Add(tmpV2)
                        Next k
                    End If
                    For k = 0 To Geometries(i).MeshParts(j).Positions.Count - 1
                        If VertSourceID > -1 Then
                            IDs.Vertex = k
                        Else
                            IDs.Vertex = -999
                        End If
                        If NormSourceID > -1 Then
                            IDs.Normal = k
                        Else
                            IDs.Normal = -999
                        End If
                        If TextSourceID > -1 Then
                            IDs.TextCoord = k
                        Else
                            IDs.TextCoord = -999
                        End If
                        MO(MeshCt).Indices.Add(IDs)
                    Next k
                Next j
            Next i
            Return MO
        End Function

        Private Function CalculateIntFromSingle(value As Single) As Integer
            Dim Int As Integer
            Int = CInt(255 * value)
            Return Int
        End Function

        Private Function ExtractSourceData(ByVal XMLInnerText As String, ByRef Data() As Single) As Integer
            Dim i As Integer
            Dim s() As String
            s = Split(Trim(Replace(XMLInnerText, "  ", " ")), " ")
            ReDim Data(s.Count - 1)
            For i = 0 To s.Count - 1
                Data(i) = CSng(s(i))
            Next i
            Return s.Count - 1
        End Function

    End Class

End Namespace